IMX6ULL学习笔记(一) —— 寄存器组
IMX6ULL 学习笔记
version : v1.0 「2023.4.27」
author: Y.Z.T.
简介: 随记, 关于I.MX6ULL 系统SOC 的部分寄存器
1️⃣ 裸机
简单了解原理
1.1 GUN汇编
1.1.1 与ARM
汇编的伪操作差异
1.1.2 常见GUN ARM
伪操作指令
补充数据传输指令
1.1.3
在
GUN ARM
汇编语言中 , 标号_start
是汇编程序的入口 , 如果希望这个标号被其他文件引用 , 只需要在定义的地方使用.globa
伪操作声明即可 (.globa
表示定义一个全局标号 )
1
2
3 .globa -start
...
.section
伪操作
可以通过
.section
伪操作自定义一个段
1
2
3
4 .section <section name> {,"<flags>"}
.section .mysection "awx" @注释 : 定义一个可写、可执行的段
.align 2
- 注意在使用伪操作
.section
定义一个段时,每个段 以段名开始 ,以 下一个段名或文件结尾 作为结束标记。- 在定义段名时,注意不要和系统预留的段名冲突,(如
.text
、.data
、.bss
、.rodata
)
系统预留的段名
1.1.4 基本数据格式
在定义数据的过程中需要注意 :
- 二进制 数据通常以
0B
或0b
开头- 八进制 数据以
0
开头- 十六进制 数据以
0x
开头- 十进制 数据则以
非0
数字开头- 负数 前面加
-
- 取补 用
~
- 不相等 用
〈〉
- 其他运算符号如
+、-、*、%、<、<<、\>、>>、|、&、^、!、==、>=、&&
与C语言语法相似- 字符串常量 要用双引号
""
括起来- 使用
.ascii
定义字符串时要自行在结尾加\0
.string
伪操作可以定义多个字符串.asciz
伪操作可以定义一个以NULL
字符结尾的字符串- 用
.rep
t 伪操作可以重复定义数据- 在
GNU ARM
汇编程序中经常使用小圆点.
表示 当前指令的地址
1
2
3
4 .ascii "hello\0"
.string "hello", "world!"
.asciz "hello"
.rept 3 .byte 0x10 .endr
1.1.5 .equ 伪操作
使用 伪操作 定义浮点数
1
2
3
4 @标签: 命令
f:
.float 3.14
.equ f,3.1415
- 上面通过使用
.float
伪操作定义一个浮点数f
,并初始化为3.14
- 并通过
.equ
伪操作 将浮点数重新赋值成3.1415
.equ
伪操作除了给数据赋值,还可以把常量定义在代码段中,然后在代码中直接引用。类似C语言中的#define
宏定义
1
2
3
4
5
6
7
8 .section .data
.equ DELAY,100
...
.section .text
...
MOV R0,$DELAY
...
1.1.6 补充
stmdb
和ldmia
- stmdb和ldmia指令一般配对使用
- stmdb用于将寄存器压栈
- ldmia用于将寄存器弹出栈
- 它们的作用是保存使用到的寄存器
ARM指令的多数据传输(STM、LDM)中,提到:多寄存器的Load和Store指令分为2组:
- 一组用于数据的存储与读取,对应于IA、IB、DA、DB,
- 一组用于堆栈操作,对应于FD、ED、FA、EA,
STMIB
(地址先增而后完成操作)、STMFA
(满递增堆栈);LDMIB
、LDMED
;STMIA
(完成操作而后地址递增)、STMEA
(空递增堆栈);LDMIA
、LDMFD
;STMDB
(地址先减而后完成操作)、STMFD
(满递减堆栈);LDMDB
、LDMEA
;STMDA
(完成操作而后地址递减)、STMED
(空递减堆栈);LDMDA
、LDMFA
。
IA
模式表示:每次传送后地址+4;(After Increase)
DB
模式表示:每次传送前地址-4;(Before Decrease)
1.2 IO复用表
在《imx6ull 中文参考手册》 199页
可以看见 :
- GPIO1 有 32 个 IO
- GPIO2 有 22 个 IO
- GPIO3 有 29 个 IO
- GPIO4 有 29 个 IO
- GPIO5 有 12 个 IO
1.3 复用为GPIO时的寄存器
1.3.1 IO复用功能和IO电气属性
-
SW_MUX_CTL_PAD_*
: 配置IO口的复用功能以
GPIO1_IO1
举例:- 可以看到
GPIO1_IO01
可以复用为8种不同功能的IO
- 可以看到
-
SW_PAD_CTL_PAD_*
: 配置IO口的电气属性还是以
GPIO1_IO00
举例:- 用于配置
IO
的 上下拉电阻、IO速度、IO驱动能力、压摆率等电气属性 - 一般主要设置 压摆率 (SRE)、驱动能力 (DSE)、速度 (SPEED )、上下拉 ( PUS)、 开漏 (ODE)
- 用于配置
1.3.2 复用为GPIO时其他寄存器
- DR寄存器: 数据寄存器
- 寄存器中的每一位对应一个IO
- 当 GPIO 被配置为 输出功能 以后 , 向指定的位写入数据那么相应的 IO 就会输出相应的高低电平 ( 如 :
GPIO1_IO00
输出高电平,那么就应该设置GPIO1.DR=1
) - 当 GPIO 被配置为 输出功能 以后 , 此寄存器就保存着对应 IO 的电平值 (如 : 当
GPIO1_IO00
这个引脚接地的话,那么GPIO1.DR
的bit0
就是 0)
- GDIR 寄存器 : 方向寄存器
- 寄存器中的每一位对应一个IO , 用来设置某个IO的工作方向
- 如果要设置一个GPIO为输入 , 则对应的位设置为 0 (如 : 要设置
GPIO1_IO00
为输入,那么GPIO1.GDIR=0
) - 如果要设置一个GPIO为输出 , 则对应的位设置为 1
- PSR 寄存器 : 状态寄存器
- 寄存器中的每一位对应一个IO , 获取对应
GPIO
的状态 - 读取相应的位即可获取对应的 GPIO 的状态,也就是 GPIO 的高低电平值 ( 功能和 输入状态 下的
DR
寄存器一样。 )
- 寄存器中的每一位对应一个IO , 获取对应
-
ICR1 和 ICR2寄存器 : 中断控制寄存器
-
寄存器中的每两位对应一个IO ,
ICR1
用于配置 低16个GPIO (IO0 ~ IO15) , ICR2 用于配置 高 16 个 GPIO ( IO16~ IO31 ) -
这两个位用来配置中断的触发方式
举例: 设置
GPIO1_IO15
为上升沿触发中断 (GPIO1.ICR1=2<<30
)
-
- IMR 寄存器 : 中断使能寄存器
- 寄存器中的每一位对应一个IO , 用来控制 GPIO 的中断禁止和使能
- 如果 使能 某个 GPIO 的中断,那么设置相应的位为 1 ; ( 如: 使能
GPIO1_IO00
的中断,那么就可以设置GPIO1.MIR=1
) - 禁止中断 , 则设置相应的位 0
- ISR 寄存器 : 中断状态寄存器
- 寄存器中的每一位对应一个IO , 用于判断
GPIO
中断是否发生 - 只要某个 GPIO 的中断发生 , 那么ISR中相应的位就会被 置1
- 在处理完中断之后 , 必须清除中断标志位
- 寄存器中的每一位对应一个IO , 用于判断
- EDGE_SEL 寄存器 : 边沿选择寄存器
- 寄存器中的每一位对应一个IO , 用来设置边沿中断
- 这个寄存器会覆盖
ICR1
和ICR2
的设置 - 如果相应的位被 置 1,那么就相当与设置了对应的 GPIO 是 上升沿和下降沿(双边沿) 触发
- ( 例如 : 设置
GPIO1.EDGE_SEL=1
,那么就表示GPIO1_IO01
是双边沿触中断 )
1.4 CCM时钟控制寄存器 (CCM_CCGRx)
CCM_CCGRx
寄存器 用于使能各个外设模块的时钟 , 为每2位对应一个外设时钟CGR值如下所示:
1.4.1 CCM_CCGR0
地址 : 0x 20C_4068H
使能方式 :
以GPIO2的时钟为例 ( CCM_CCGR0 = 3<<30 )
1.4.2 CCM_CCGR1
地址 :
0x20C_406C
H
1.4.3 CCM_CCGR2
地址 :
0x 20C_4070
H
1.4.4 CCM_CCGR3
地址
0x20C_4074
H
1.4.5 CCM_CCGR4
地址 :
0x20C_4078
H
1.4.6 CCM_CCGR5
地址 :
0x20C_407C
H
1.4.7 CCM_CCGR6
地址 :
0x20C_4080
H
1.5 IO复用为GPIO
一般步骤:
- 使能
GPIO
对应的时钟 (CCM_CCGRx
寄存器) - 设置寄存器
IOMUXC_SW_MUX_CTL_PAD_XX_XX
,设置 IO 的复用功能 - 设置寄存器
IOMUXC_SW_PAD_CTL_PAD_XX_XX
,设置 IO 的上下拉、速度等电气属性 - 配置GPIO专属寄存器 , 配置GPIO的 输入输出 、是否使用中断 等 (
DR
、GDIR
、ICR
等寄存器)
1.6 可执行文件
将裸机的汇编点灯程序编译成可执行文件
.bin
, 需要在.bin
文件前面添加 头部信息包括 镜像向量表 (
IVT
) 、Boot启动数据 (Boot data
)、 配置信息 (DCD
)等 , 后面才是 用户可执行代码 (Application
)
如下所示:
不同引导设备的 偏移量 与 加载区域大小 ( 即 头部信息)
- 则
IVT + Boot Data + DCD
的大小为 加载区域大小 减去 偏移量 (4K Byte - 1K Byte = 3K Byte
)
在
IVT
和Boot Data
中存放着各个 部分的 链接地址如 :
entry
存放的是镜像第一行指令所在的位置 , 即 用户执行代码 的入口 , 可以看到箭头指向了Application
self
存放的是IVT
复制到DDR
中以后的 首地址 , 可以看到箭头目的地址指向IVT
的开头
1.6.1 各部分起始地址
在裸机点灯的程序中, 链接的地址是 0x87800000
即 用户执行代码 (
Application
) 的 起始地址entry = 0x87800000
镜像向量表 (
IVT
) 的起始地址self = 0x877FF400
[0x87800000 - 3k byte = 0x877FF400
]
Boot
启动数据 (Boot Data
) 的起始地址boot data = 0x877FF420
- [ 此处是
IVT
的地址 加上IVT
的长度0x877FF400 + 32 Byte = 0x877FF420
]
配置信息 (
DCD
) 的起始地址dcd = 0x877FF42C
- [ 此处是
Boot Data
的地址 加上Boot data
的长度0x877FF420 + 12 Byte = 0x877FF42C
]
整个头部信息的 起始地址
start = 0X877FF000
- [ 此处是 IVT 的起始地址 减去 1K Byte 的偏移量
0x877FF400 - 1K Byte = 0X877FF000
]
1.6.2 镜像向量表
镜像向量表 ( IVT
) 存放了 8条 信息 , 每条信息大小都是 32位
IVT 存放的内容:
header 存放格式:
Tag
为一个字节长度,值固定为0XD1
Length
为 大端模式 的两字节字段 , 存放着IVT
的 长度信息 , IVT 长度固定为32字节 (0x0020
) , 则Length = 0x2000
Version
为单字节字段 , 值为0x40
或0x41
1.6.3 Boot Data
Boot Data
存放了 3条 信息 , 每条信息大小都是 32位
start
存放的是整个头部信息的存放地址 , 包含偏移量length
存放的是整个镜像 文件的大小plugin
插件
1.6.4 DCD数据
DCD 存放 程序镜像中包含的各种配置信息 ( 即
IMX6ULL
寄存器地址 和对应 配置信息 的集合 )因为复位芯片后 ,
I.MX6U
片内的所有寄存器都会复位为默认值,但是这些默认值往往不是我们想要的值 , 通过在DCD
中添加这部分寄存器的配置信息 ( 如系统时钟 ) ,Boot ROM
会使用这些寄存器地址和配置集合来初始化相应的寄存器DCD 区域不能超过 1768Byte
DCD格式 :
Header格式
Tag
是单字节,固定为0XD2
Length
为 大端模式 的两字节字段 , 存放着DCD
的 长度信息Version
为单字节 , 固定为0x41
CMD 格式:
CMD 包含 写数据命令 、检查数据命令、NOP命令、解锁命令
其中 写数据命令 格式如下:
- Tag 是单字节,固定为
0xCC
Length
为 大端模式 的两字节字段 , 存放着 写入数据 的 长度信息CMD
是命令 , 包含header
Address
是地址 , 存放着写入数据的 目标地址 ( 即寄存器地址 )Value / Mask
值或掩码 , 存放着前一个 地址 (Address
) 的数据值 ( 即寄存器值)
Parameter
参数 的格式如下:
Parameter
为单字节数据bytes
表示是目标位置宽度,单位为byte
,可以选择 1、2、和 4 字节flags
是命令控制标志位
例:
1.7 点灯(GPIO复用测试)
1.7.1 启动文件(start.S)
1 | // 定义全局入口符号 |
1.7.2 源文件与头文件
main.c && led.c
1 | /******************************** main.c ******************************/ |
main.h $$ led.h
1 | /******************************** led.h ******************************/ |
1.7.3 Makefile
1 | CC =arm-linux-gnueabihf |
1.7.4 链接脚本
1 | SECTIONS{ |
- 使用关键字
SECTIONS
, 描述输出文件的内存布局.
是 定位计数器 , 其值表示以其为地址开始 , 这里是以0x87800000
为起始地址.text
.rodata
.data
. bss
分别是 代码段 、只读数据段、数据段、bss段- 这几个段名冒号后面的
ALIGN(4)
表示以 四字节对齐 , 大括号里面表示输入文件 ( 即将什么文件 放入哪一段)__bss_start
和__bss_end
相当于两个变量 , 其值均为定位符.
, 分别报存了bss
段 的起始地址 和 结束地址 , 方便对bss
段进行清零
1.8 时钟树
时钟树由三部分组成:时钟切换控制器,根时钟产生器,系统时钟
时钟切换控制器: 用来将外部晶振进行倍频,以实现稳定且高频的时钟信号。
根时钟产生器: 在时钟切换控制器配置完成之后的输出时钟就成了根时钟产生器的时钟源,在根时钟产生器中,经过寄存器配置之后,就成为了外设时钟的时钟源
一个外设的时钟信号的产生途径:晶振向芯片输入时钟信号,信号进入时钟切换控制器,经过用户配置,产生
PLL
时钟信号,该信号进入根时钟产生步骤,经过分频或倍频,最终成为系统某个外设的时钟信号。
主时钟生成
ARM_PLL(PLL1)
,此路PLL
是供ARM
内核使用的,ARM
内核时钟就是由此PLL
生成的,此PLL
通过编程的方式最高可倍频到1.3GHz
528_PLL(PLL2)
,此路PLL
也叫做System_PLL
,此路PLL
是固定的 22 倍频 , 即528MHZ
- 此PLL 有四路PFD :
PLL2_PFD0
、PLL2_PFD1
、PLL2_PFD2
、PLL2_PFD3
, 这些PFD
以PLL
为基础,经过不同的分频系数产生不同频率的时钟- 官方推荐配置参数
USB1_PLL(PLL3)
,此路PLL
主要用于USB1PHY
, 此路PLL
是固定的20倍频 , 即480MHZ
- 此PLL 也有四路PFD :
PLL3__PFD0
、PLL3__PFD1
、PLL3__PFD2
、PLL3__PFD3
- 官方推荐配置参数
USB2_PLL(PLL7)
主要用于USB2PHY
,此路PLL
固定为20倍频,因此也是480MHz
ENET_PLL(PLL6)
, 此路PLL
固定为20+5/6
倍频 , 即480MHz
VIDEO_PLL(PLL5)
用于显示相关的外设 ,PLL
的输出范围在650MHz~1300MHz
, 可选1/2/4/8/16
分频
AUDIO_PLL(PLL4)
用于音频相关的外设 ,PLL
的输出范围在650MHz~1300MHz
, 可选1/2/4
分频
时钟树
- 橙色梯形的是 选择器 ,可以选择不同的
PLL
时钟作为 时钟源- 矩形就是 分频器(2位,3位,6位)
- 黄色高亮的就是用来配置 选择器 的各种 寄存器
- 红色字是用来配置 分频系数 的 , 就是配置 寄存器的某个几位<>